package com.g001.service.ligthing;

import java.awt.Point;
import java.util.ArrayList;
import java.util.Collections;

import org.newdawn.slick.Color;
import org.newdawn.slick.Graphics;
import org.newdawn.slick.SlickException;
import org.newdawn.slick.opengl.Texture;
import org.newdawn.slick.opengl.TextureImpl;
import org.newdawn.slick.opengl.renderer.LineStripRenderer;
import org.newdawn.slick.opengl.renderer.Renderer;

/**
 * Generates a 2D lightning bolt graphic in Slick.
 *
 * @author davedes
 */

public class LightningBolts {

    protected static LineStripRenderer LSR = Renderer.getLineStripRenderer();

    private FX[] fx;

    private int fxPointer;

    public static final Color GLOW_COLOR = new Color(0.75f, 0.75f, 1f, 0.25f); //Bolt outer-glow colour
    public static final Color LINE_COLOR = new Color(0.95f, 0.95f, 1f); //Bolt line colour

    private float[] alphas = new float[20];
    private float masterAlpha = 1f;

    public static final int JITTER_DELAY = 12;
    private int jitterDelay = JITTER_DELAY;
    private boolean idle = true;

    private float count;

    public LightningBolts() throws SlickException {
    }

    public void Zapper(float originX, float originY, ArrayList<Point> pts) {
        Collections.shuffle(pts);
        init(pts.size());
        float lastOriginX = originX;
        float lastOriginY = originY;
        for (int i = 1; i != pts.size(); i++) {
            Point p = pts.get(i);
            zap(lastOriginX, lastOriginY, (float)p.getX() + 25f, (float)p.getY() + 25f);
            lastOriginX = (float) (p.getX() + 25);
            lastOriginY = (float) (p.getY() + 25);
        }
    }

    class FX extends Lightning {

        float alpha = 1f;
        float driftX;
        float driftY;
        float angle = 0f;

        private float lastZapX, lastZapY, x, y;

        boolean visible = false;

        public void generate(float x1, float y1, float x2, float y2) {
            driftX = rnd(-0.005f, 0.005f);
            driftY = rnd(-0.005f, 0.005f);
            alpha = 1f;
            super.generate(x1, y1, x2, y2);
            angle = (float) Math.toDegrees(Math.atan2(y2 - y1, x2 - x1)) - 90;
        }
    }

    public void init(int pts) {

        fx = new FX[pts];

        for (int i = 0; i < fx.length; i++)
            fx[i] = new FX();

        for (int i = 0; i < alphas.length; i++)
            alphas[i] = Lightning.rnd(0.5f, 1f);

        idle = false;
        fxPointer = 0;
        count = 0;

    }


    public void render(Graphics g) throws SlickException {
        g.setAntiAlias(true);
        if (fx != null) {
            for (FX aFx : fx) {
                if (aFx.generated() && aFx.visible && aFx.alpha > 0f) {
                    renderFX(g, aFx);
                }
            }
        }
    }

    private float alpha(int index) {
        if (index > alphas.length - 1)
            index = alphas.length - index;
        return alphas[index];
    }

    private void drawBoltLine(Graphics g, Lightning.Bolt b, Color color, float thickness, float alpha) {
        Color c = new Color(color);
        c.a *= alpha;
        g.setColor(c);
        g.setLineWidth(thickness);
        Texture t = TextureImpl.getLastBind();
        TextureImpl.bindNone();
        LSR.start();
        for (int i = 0; i < b.vertices(); i++) {
            Lightning.Vertex v = b.getVertex(i);
            LSR.vertex(v.x, v.y);
            //for a cool effect: fade out the end of the line using LSR.color !
        }
        LSR.end();
        if (t == null) {
            TextureImpl.bindNone();
        } else {
            t.bind();
        }
    }

    private void jitterFX(int delta, FX fx) {
        jitterDelay -= delta;
        if (jitterDelay < 0) {
            jitterDelay = JITTER_DELAY;
            Lightning.Bolt mainBolt = fx.getMainBolt();
            for (int i = 0; i < mainBolt.children(); i++) {
                //get last child
                Lightning.Bolt child = mainBolt.getChild(i);
                for (int j = 0; j < child.children(); j++) {
                    Lightning.Bolt subChild = child.getChild(j);
                    subChild.generate();
                }
            }
        }
    }

    private void renderFX(Graphics g, FX fx) {

        Lightning.Vertex origin = fx.getPointA();

        Lightning.Bolt mainBolt = fx.getMainBolt();
        GLOW_COLOR.a = 0.5f;
        LINE_COLOR.a = 1f;

        g.rotate(origin.x, origin.y, fx.angle);
        g.translate(origin.x, origin.y);

        //we want full control over each level (3 layers of bolts)
        //so we will render from bottom-up
        float t = 2f;
        for (int i = 0; i < mainBolt.children(); i++) {
            Lightning.Bolt child = mainBolt.getChild(i);
            g.translate(child.x, child.y);
            g.rotate(0, 0, child.angle);

            for (int j = 0; j < child.children(); j++) {
                Lightning.Bolt subChild = child.getChild(j);

                g.translate(subChild.x, subChild.y);
                g.rotate(0, 0, subChild.angle);

                float a = alpha(j) * 0.5f * masterAlpha * fx.alpha;
                drawBoltLine(g, subChild, GLOW_COLOR, t * 3, a * 0.25f);
                drawBoltLine(g, subChild, LINE_COLOR, t, a);

                g.rotate(0, 0, -subChild.angle);
                g.translate(-subChild.x, -subChild.y);
            }


            float a = alpha(i) * 0.75f * masterAlpha * fx.alpha;
            drawBoltLine(g, child, GLOW_COLOR, t * 3, a * 0.45f);
            drawBoltLine(g, child, LINE_COLOR, t, a);

            g.rotate(0, 0, -child.angle);
            g.translate(-child.x, -child.y);
        }

        t = 1.7f;
        float a = alpha(0) * 1f * masterAlpha * fx.alpha;
        drawBoltLine(g, mainBolt, GLOW_COLOR, t * 3, a * 0.45f);
        drawBoltLine(g, mainBolt, LINE_COLOR, t, a);

        g.translate(-origin.x, -origin.y);
        g.rotate(origin.x, origin.y, -fx.angle);
    }

    private void zap(float originX, float originY, float x, float y) {
        fxPointer++;
        if (fxPointer > fx.length - 1) {
            idle = true;
            fxPointer = 0;
            return;
        }
        fx[fxPointer].generate(originX, originY, x, y);
        fx[fxPointer].x = x;
        fx[fxPointer].y = y;
        fx[fxPointer].lastZapX = x;
        fx[fxPointer].lastZapY = y;
    }

    public void update(int delta) throws SlickException {
        count += 0.50;
        boolean cleanup = true;
        if (fx != null) {
            for (FX aFx : fx) {
                if (!aFx.visible && count % 1 == 0) {
                    aFx.visible = true;
                    break;
                }
            }

            for (FX aFx : fx) {
                if (aFx.generated() && aFx.visible && aFx.alpha > 0f) {
                    aFx.pointA.x += aFx.driftX;
                    aFx.pointA.y += aFx.driftY;
                    aFx.alpha -= 0.05f;
                    if (aFx.alpha < 0f)
                        aFx.alpha = 0f;
                    jitterFX(delta, aFx);
                }
                if (fx[fxPointer].alpha != 0f)
                    cleanup = false;
            }

            if (!idle && fx[fxPointer].alpha < 0.5f)
                zap(fx[fxPointer].lastZapX, fx[fxPointer].lastZapY, fx[fxPointer].x, fx[fxPointer].y);

        }
        if (cleanup)
            fx = null;
    }

}